#include "FluidField.h"

FluidField::FluidField( int w, int h) {
	mVel.resize( w * h );
	std::fill(mVel.begin(), mVel.end(), glm::vec3(0, 0, 0));
	mPress.resize( w * h );
	std::fill(mPress.begin(), mPress.end(), 0.0 );

	mDim.x = w; mDim.y = h;

}

FluidField & FluidField::operator=( const FluidField & rhs ){
	if (this != &rhs){
		this->mDim = rhs.mDim;
		this->mVel = rhs.mVel;
		this->mPress = rhs.mPress;
		
	}

	return *this;
}

float FluidField::GetPress(unsigned int u, unsigned int v){
	return mPress[v*mDim.x + u];
}

void FluidField::SetPress(unsigned int u, unsigned int v, float val){
	mPress[v*mDim.x + u] = val;
}

float * FluidField::GetVel(unsigned int u, unsigned int v) {
		return &(mVel[v*mDim.x + u])[0];
}

float * FluidField::GetVel(float x, float y){
	return &(mVel[(unsigned int)(y*mDim.y)*mDim.x + (unsigned int)(x*mDim.x)])[0];
}

void FluidField::SetVel( float x, float y, float * inData ) {
	int ndx = (unsigned int)(y*mDim.y)*mDim.x + (unsigned int)(x*mDim.x);
	std::copy(inData, inData+3, &(mVel[ndx])[0]);
}

void FluidField::SetVel( unsigned int u, unsigned int v, float * inData ) {
	int ndx = v*mDim.x + u;
	std::copy(inData, inData+3, &(mVel[ndx])[0]);
}

void FluidField::SampleField( float x, float y, float * out ){
	float perc[2] = { (x*mDim.x), 
		(y*mDim.y) };
	unsigned int cellNdx[2] = { (unsigned int)(perc[0]), (unsigned int)(perc[1]) };
	float cellwidth = 1.0f/(mDim.x);
	BilinInterp(cellNdx[0], cellNdx[1], (perc[0] - (float)(cellNdx[0])), (perc[1] - (float)(cellNdx[1])), out);
}

void FluidField::BilinInterp( unsigned int cellU, unsigned int cellV, float u, float v , float * out ){
	// Get the velocities at the cell's corners.
	int cOffset[2];

	cOffset[0] = (u <= 0.5f) ? -1 : 1;
	cOffset[1] = (v <= 0.5f) ? -1 : 1;

	//left, right, bottom, top
	int sampleNdx[4][2] = 
	{
		{(cOffset[0] < 0) ? cellU-1 : cellU, (cOffset[1] < 0 ? cellV-1 : cellV) },
		{(cOffset[0] < 0) ? cellU : cellU + 1, (cOffset[1] < 0 ? cellV-1 : cellV)},
		{(cOffset[0] < 0) ? cellU : cellU + 1, (cOffset[1] < 0 ? cellV : cellV+1) },
		{(cOffset[0] < 0) ? cellU-1 : cellU,(cOffset[1] < 0 ? cellV : cellV+1)}
	};

	// clamp
	for (int i = 0; i <4; ++i){
			sampleNdx[i][0] = std::max(std::min(mDim.x-1, sampleNdx[i][0]), 0);
			sampleNdx[i][1] = std::max(std::min(mDim.y-1, sampleNdx[i][1]), 0);
	}
	
	// Translate over
	float su = u > 0.5 ? u- 0.5f : u+0.5f,
		sv = v > 0.5 ? v - 0.5f : v+0.5f;

	BilerpV3( GetVel((unsigned int)sampleNdx[0][0], sampleNdx[0][1]), GetVel((unsigned int)sampleNdx[1][0], sampleNdx[1][1]),
		GetVel((unsigned int)sampleNdx[2][0], sampleNdx[2][1]), GetVel((unsigned int)sampleNdx[3][0], sampleNdx[3][1]),
		su, sv, out);
}

void FluidField::BilerpV3( float * lowerL, float * lowerR, float * upperR, float * upperL, float u, float v, float * out){
	for (int i = 0; i < 3; ++i){
		out[i] = (1-v)*((1-u)*lowerL[i] + (u)*lowerR[i]) +
			v * ((1-u)*upperL[i] + (u)*upperR[i]);
	}
}



void FluidField::Getdim( int & dimX, int & dimY ) {
	dimX = mDim.x;
	dimY = mDim.y;
}

unsigned int FluidField::GetDimW() const{ return mDim.x; }

unsigned int FluidField::GetDimH() const{ return mDim.y; }




